package org.hrqing.chess.service;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.hrqing.chess.frame.CenterBackgroundPanel;
import org.hrqing.chess.frame.ChessLabel;
import org.hrqing.chess.frame.MainFrame;
import org.springframework.stereotype.Service;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Hrqing
 */
@Slf4j
@Service
public class GameService {

    @Getter
    @Setter
    @AllArgsConstructor
    public static class Coordinate {
        private ChessLabel.Color color;
        private ChessLabel.Purpose purpose;
        private int x;
        private int y;

        public ChessLabel.Color enemyColor() {
            return this.color.equals(ChessLabel.Color.RED) ? ChessLabel.Color.BLACK : ChessLabel.Color.RED;
        }

    }

    @Getter
    @Setter
    @AllArgsConstructor
    public static class SpecialCoordinate {
        private int x;
        private int y;
        private int sx;
        private int sy;

    }

    private Coordinate coordinate(ChessLabel chess) {
        String[] split = chess.getId().split("-");
        return new Coordinate(chess.getColor(), chess.getPurpose(), Integer.parseInt(split[0]), Integer.parseInt(split[1]));
    }

    private List<Coordinate> coordinates(CenterBackgroundPanel panel) {
        List<Coordinate> coordinates = new ArrayList<>();
        for (Component component : panel.getComponents()) {
            if (component instanceof ChessLabel label) {
                coordinates.add(coordinate(label));
            }
        }
        log.info("components: {}", coordinates.size());
        return coordinates;
    }

    private boolean plateRange(Coordinate coordinate) {
        return coordinate.getX() > 0 && coordinate.getY() > 0;
    }

    public boolean rule(CenterBackgroundPanel panel, ChessLabel selected, ChessLabel current, int mx, int my) {
        List<Coordinate> coordinates = coordinates(panel);
        ChessLabel.Color color = panel.getColor();
        Coordinate coordinate;
        boolean eat = false;
        if (current == null) {
            coordinate = new Coordinate(null, null, mx, my);
        } else {
            coordinate = coordinate(current);
            eat = true;
        }
        if (!plateRange(coordinate)) {
            return false;
        }
        switch (selected.getPurpose()) {
            case PAWN -> {
                return movePawn(selected, coordinate, color.equals(ChessLabel.Color.RED));
            }
            case CANNON -> {
                return moveCannon(selected, coordinate, coordinates, eat);
            }
            case ROCK -> {
                return moveRock(selected, coordinate, coordinates);
            }
            case KNIGHT -> {
                return moveKnight(selected, coordinate, coordinates);
            }
            case BISHOP -> {
                return moveBishop(selected, coordinate, coordinates, color.equals(ChessLabel.Color.RED));
            }
            case GUARD -> {
                return moveGuard(selected, coordinate, color.equals(ChessLabel.Color.RED));
            }
            case KING -> {
                return moveKing(selected, coordinate, color.equals(ChessLabel.Color.RED));
            }
        }
        return false;
    }

    private boolean moveKing(ChessLabel selected, Coordinate coordinate, boolean front) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 直线移动
        if (selectedCoordinate.getX() != coordinate.getX() && selectedCoordinate.getY() != coordinate.getY()) {
            return false;
        }
        int absoluteX = absolute(coordinate.getX() - selectedCoordinate.getX());
        int absoluteY = absolute(coordinate.getY() - selectedCoordinate.getY());
        // 不能超过1的移动量
        if (absolute(absoluteX - absoluteY) > 1) {
            return false;
        }
        if (front) {
            switch (selected.getColor()) {
                case RED -> {
                    return coordinate.getX() >= 4 && coordinate.getX() <= 6
                            && coordinate.getY() >= 8 && coordinate.getY() <= 10;
                }
                case BLACK -> {
                    return coordinate.getX() >= 4 && coordinate.getX() <= 6
                            && coordinate.getY() >= 1 && coordinate.getY() <= 3;
                }
            }
        } else {
            switch (selected.getColor()) {
                case RED -> {
                    return coordinate.getX() >= 4 && coordinate.getX() <= 6
                            && coordinate.getY() >= 1 && coordinate.getY() <= 3;
                }
                case BLACK -> {
                    return coordinate.getX() >= 4 && coordinate.getX() <= 6
                            && coordinate.getY() >= 8 && coordinate.getY() <= 10;
                }
            }

        }
        return false;
    }

    private boolean moveGuard(ChessLabel selected, Coordinate coordinate, boolean front) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 不能直线移动
        if (selectedCoordinate.getX() == coordinate.getX() || selectedCoordinate.getY() == coordinate.getY()) {
            return false;
        }
        if (front) {
            switch (selected.getColor()) {
                case RED -> {
                    if (selectedCoordinate.getX() == 4 || selectedCoordinate.getX() == 6) {
                        return coordinate.getX() == 5 && coordinate.getY() == 9;
                    }
                    if (selectedCoordinate.getX() == 5) {
                        return (coordinate.getX() == 4 && coordinate.getY() == 10)
                                || (coordinate.getX() == 4 && coordinate.getY() == 8)
                                || (coordinate.getX() == 6 && coordinate.getY() == 10)
                                || (coordinate.getX() == 6 && coordinate.getY() == 8);
                    }
                }
                case BLACK -> {
                    if (selectedCoordinate.getX() == 4 || selectedCoordinate.getX() == 6) {
                        return coordinate.getX() == 5 && coordinate.getY() == 2;
                    }
                    if (selectedCoordinate.getX() == 5) {
                        return (coordinate.getX() == 4 && coordinate.getY() == 1)
                                || (coordinate.getX() == 4 && coordinate.getY() == 3)
                                || (coordinate.getX() == 6 && coordinate.getY() == 1)
                                || (coordinate.getX() == 6 && coordinate.getY() == 3);
                    }
                }
            }
        } else {
            switch (selected.getColor()) {
                case RED -> {
                    if (selectedCoordinate.getX() == 4 || selectedCoordinate.getX() == 6) {
                        return coordinate.getX() == 5 && coordinate.getY() == 2;
                    }
                    if (selectedCoordinate.getX() == 5) {
                        return (coordinate.getX() == 4 && coordinate.getY() == 1)
                                || (coordinate.getX() == 4 && coordinate.getY() == 3)
                                || (coordinate.getX() == 6 && coordinate.getY() == 1)
                                || (coordinate.getX() == 6 && coordinate.getY() == 3);
                    }
                }
                case BLACK -> {
                    if (selectedCoordinate.getX() == 4 || selectedCoordinate.getX() == 6) {
                        return coordinate.getX() == 5 && coordinate.getY() == 9;
                    }
                    if (selectedCoordinate.getX() == 5) {
                        return (coordinate.getX() == 4 && coordinate.getY() == 10)
                                || (coordinate.getX() == 4 && coordinate.getY() == 8)
                                || (coordinate.getX() == 6 && coordinate.getY() == 10)
                                || (coordinate.getX() == 6 && coordinate.getY() == 8);
                    }
                }
            }
        }
        return false;
    }

    private boolean moveBishop(ChessLabel selected, Coordinate coordinate, List<Coordinate> coordinates, boolean front) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 不能直线移动
        if (selectedCoordinate.getX() == coordinate.getX() || selectedCoordinate.getY() == coordinate.getY()) {
            return false;
        }
        // 不能过界
        if (front) {
            switch (selected.getColor()) {
                case RED -> {
                    if (coordinate.getY() < 6) {
                        return false;
                    }
                }
                case BLACK -> {
                    if (coordinate.getY() > 5) {
                        return false;
                    }
                }
            }
        } else {
            switch (selected.getColor()) {
                case RED -> {
                    if (coordinate.getY() > 5) {
                        return false;
                    }
                }
                case BLACK -> {
                    if (coordinate.getY() < 6) {
                        return false;
                    }
                }
            }
        }
        List<SpecialCoordinate> bishopCoordinates = bishopCoordinates(selectedCoordinate);
        Optional<SpecialCoordinate> first = bishopCoordinates.stream().filter(filter -> filter.getX() == coordinate.getX() && filter.getY() == coordinate.getY()).findFirst();
        if (first.isPresent()) {
            SpecialCoordinate currentCoordinate = first.get();
            List<Coordinate> list = coordinates.stream().filter(filter -> filter.getX() == currentCoordinate.getSx() && filter.getY() == currentCoordinate.getSy()).toList();
            return list.isEmpty();
        }
        return false;
    }

    private boolean moveKnight(ChessLabel selected, Coordinate coordinate, List<Coordinate> coordinates) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 不能直线移动
        if (selectedCoordinate.getX() == coordinate.getX() || selectedCoordinate.getY() == coordinate.getY()) {
            return false;
        }
        List<SpecialCoordinate> knightCoordinates = knightCoordinates(selectedCoordinate, true);
        Optional<SpecialCoordinate> first = knightCoordinates.stream().filter(filter -> filter.getX() == coordinate.getX() && filter.getY() == coordinate.getY()).findFirst();
        if (first.isPresent()) {
            SpecialCoordinate currentCoordinate = first.get();
            List<Coordinate> list = coordinates.stream().filter(filter -> filter.getX() == currentCoordinate.getSx() && filter.getY() == currentCoordinate.getSy()).toList();
            return list.isEmpty();
        }
        return false;
    }

    private boolean moveRock(ChessLabel selected, Coordinate coordinate, List<Coordinate> coordinates) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 直线移动
        if (selectedCoordinate.getX() != coordinate.getX() && selectedCoordinate.getY() != coordinate.getY()) {
            return false;
        }
        Map<Integer, List<Coordinate>> xCollect = coordinates.stream().collect(Collectors.groupingBy(Coordinate::getX));
        Map<Integer, List<Coordinate>> yCollect = coordinates.stream().collect(Collectors.groupingBy(Coordinate::getY));
        List<Coordinate> list = coordinates(coordinate, selectedCoordinate, xCollect, yCollect);
        return list.isEmpty();
    }

    private boolean moveCannon(ChessLabel selected, Coordinate coordinate, List<Coordinate> coordinates, boolean eat) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 直线移动
        if (selectedCoordinate.getX() != coordinate.getX() && selectedCoordinate.getY() != coordinate.getY()) {
            return false;
        }
        Map<Integer, List<Coordinate>> xCollect = coordinates.stream().collect(Collectors.groupingBy(Coordinate::getX));
        Map<Integer, List<Coordinate>> yCollect = coordinates.stream().collect(Collectors.groupingBy(Coordinate::getY));
        List<Coordinate> list;
        if (eat) {
            list = coordinates(coordinate, selectedCoordinate, xCollect, yCollect);
            return list.size() == 1;
        } else {
            list = coordinates(coordinate, selectedCoordinate, xCollect, yCollect);
            return list.isEmpty();
        }
    }

    private boolean movePawn(ChessLabel selected, Coordinate coordinate, boolean front) {
        Coordinate selectedCoordinate = coordinate(selected);
        // 直线移动
        if (selectedCoordinate.getX() != coordinate.getX() && selectedCoordinate.getY() != coordinate.getY()) {
            return false;
        }
        int absoluteX = absolute(coordinate.getX() - selectedCoordinate.getX());
        int absoluteY = absolute(coordinate.getY() - selectedCoordinate.getY());
        // 不能超过1的移动量
        if (absolute(absoluteX - absoluteY) > 1) {
            return false;
        }
        // 正向
        if (front) {
            switch (selected.getColor()) {
                case RED -> {
                    if (selectedCoordinate.getY() <= 5) {
                        return coordinate.getY() < selectedCoordinate.getY() || coordinate.getX() != selectedCoordinate.getX();
                    } else {
                        // 只能往前走
                        return coordinate.getY() < selectedCoordinate.getY();
                    }
                }
                case BLACK -> {
                    if (selectedCoordinate.getY() >= 6) {
                        return coordinate.getY() > selectedCoordinate.getY() || coordinate.getX() != selectedCoordinate.getX();
                    } else {
                        // 只能往前走
                        return coordinate.getY() > selectedCoordinate.getY();
                    }
                }
            }
        } else {
            switch (selected.getColor()) {
                case RED -> {
                    if (selectedCoordinate.getY() >= 6) {
                        return coordinate.getY() > selectedCoordinate.getY() || coordinate.getX() != selectedCoordinate.getX();
                    } else {
                        // 只能往前走
                        return coordinate.getY() > selectedCoordinate.getY();
                    }
                }
                case BLACK -> {
                    if (selectedCoordinate.getY() <= 5) {
                        return coordinate.getY() < selectedCoordinate.getY() || coordinate.getX() != selectedCoordinate.getX();
                    } else {
                        // 只能往前走
                        return coordinate.getY() < selectedCoordinate.getY();
                    }
                }
            }
        }
        return false;
    }

    private List<SpecialCoordinate> bishopCoordinates(Coordinate coordinate) {
        List<SpecialCoordinate> coordinates = new ArrayList<>(0);
        coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() + 2, coordinate.getX() + 1, coordinate.getY() + 1));
        coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() - 2, coordinate.getX() + 1, coordinate.getY() - 1));

        coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() + 2, coordinate.getX() - 1, coordinate.getY() + 1));
        coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() - 2, coordinate.getX() - 1, coordinate.getY() - 1));
        return coordinates;
    }

    private List<SpecialCoordinate> knightCoordinates(Coordinate coordinate, boolean positive) {
        List<SpecialCoordinate> coordinates = new ArrayList<>(0);
        if (positive) {
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 1, coordinate.getY() + 2, coordinate.getX(), coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 1, coordinate.getY() - 2, coordinate.getX(), coordinate.getY() - 1));

            coordinates.add(new SpecialCoordinate(coordinate.getX() - 1, coordinate.getY() + 2, coordinate.getX(), coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() - 1, coordinate.getY() - 2, coordinate.getX(), coordinate.getY() - 1));

            coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() + 1, coordinate.getX() + 1, coordinate.getY()));
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() - 1, coordinate.getX() + 1, coordinate.getY()));

            coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() + 1, coordinate.getX() - 1, coordinate.getY()));
            coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() - 1, coordinate.getX() - 1, coordinate.getY()));
        }else {
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 1, coordinate.getY() + 2, coordinate.getX() + 1, coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 1, coordinate.getY() - 2, coordinate.getX() + 1, coordinate.getY() - 1));

            coordinates.add(new SpecialCoordinate(coordinate.getX() - 1, coordinate.getY() + 2, coordinate.getX() - 1, coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() - 1, coordinate.getY() - 2, coordinate.getX() - 1, coordinate.getY() - 1));

            coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() + 1, coordinate.getX() + 1, coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() + 2, coordinate.getY() - 1, coordinate.getX() + 1, coordinate.getY() - 1));

            coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() + 1, coordinate.getX() - 1, coordinate.getY() + 1));
            coordinates.add(new SpecialCoordinate(coordinate.getX() - 2, coordinate.getY() - 1, coordinate.getX() - 1, coordinate.getY() - 1));
        }

        return coordinates;
    }

    private List<Coordinate> coordinates(Coordinate coordinate, Coordinate selectedCoordinate, Map<Integer, List<Coordinate>> xCollect, Map<Integer, List<Coordinate>> yCollect) {
        List<Coordinate> list;
        if (selectedCoordinate.getX() == coordinate.getX()) {
            list = xCollect.get(selectedCoordinate.getX()).stream()
                    .filter(filter -> filter.getY() > Math.min(selectedCoordinate.getY(), coordinate.getY()))
                    .filter(filter -> filter.getY() < Math.max(selectedCoordinate.getY(), coordinate.getY()))
                    .toList();
        } else {
            list = yCollect.get(selectedCoordinate.getY()).stream()
                    .filter(filter -> filter.getX() > Math.min(selectedCoordinate.getX(), coordinate.getX()))
                    .filter(filter -> filter.getX() < Math.max(selectedCoordinate.getX(), coordinate.getX()))
                    .toList();
        }
        return list;
    }

    public boolean killCheck(CenterBackgroundPanel panel, ChessLabel.Color color, ChessLabel selected, int mx, int my) {
        List<Coordinate> coordinates = coordinates(panel);
        Coordinate selectedCoordinate = coordinate(selected);
        coordinates.removeIf(predicate -> predicate.getX() == mx && predicate.getY() == my);
        coordinates.removeIf(predicate -> predicate.getX() == selectedCoordinate.getX() && predicate.getY() == selectedCoordinate.getY());
        coordinates.add(new Coordinate(selected.getColor(), selected.getPurpose(), mx, my));

        // 获取帅的位置
        for (Coordinate coordinate : coordinates.stream().filter(filter -> filter.getPurpose().equals(ChessLabel.Purpose.KING) && filter.getColor().equals(color)).toList()) {
            // 检查有没有造成将军的
            if (checkPawn(coordinate, coordinates)
                || checkCannon(coordinate, coordinates)
                || checkRock(coordinate, coordinates)
                || checkKnight(coordinate, coordinates)) {
                return true;
            }
        }
        return false;
    }

    private boolean checkKnight(Coordinate kingCoordinate, List<Coordinate> coordinates) {
        List<Coordinate> list = coordinates.stream().filter(filter -> filter.getPurpose().equals(ChessLabel.Purpose.KNIGHT) && filter.getColor().equals(kingCoordinate.enemyColor()))
                .filter(filter -> filter.getX() != kingCoordinate.getX() && filter.getY() != kingCoordinate.getY())
                .toList();
        List<SpecialCoordinate> knightCoordinates = knightCoordinates(kingCoordinate, false);
        for (Coordinate coordinate : list) {
            Optional<SpecialCoordinate> first = knightCoordinates.stream().filter(filter -> filter.getX() == coordinate.getX() && filter.getY() == coordinate.getY()).findFirst();
            if (first.isPresent()) {
                SpecialCoordinate currentCoordinate = first.get();
                List<Coordinate> blocks = coordinates.stream().filter(filter -> filter.getX() == currentCoordinate.getSx() && filter.getY() == currentCoordinate.getSy()).toList();
                return blocks.isEmpty();
            }
        }
        return false;
    }

    private boolean checkRock(Coordinate kingCoordinate, List<Coordinate> coordinates) {
        List<Coordinate> list = coordinates.stream().filter(filter -> filter.getPurpose().equals(ChessLabel.Purpose.ROCK) && filter.getColor().equals(kingCoordinate.enemyColor()))
                .filter(filter -> filter.getX() == kingCoordinate.getX() || filter.getY() == kingCoordinate.getY())
                .toList();
        for (Coordinate coordinate : list) {
            if (coordinate.getX() == kingCoordinate.getX()) {
                List<Coordinate> coordinateList = coordinates.stream()
                        .filter(filter -> filter.getX() == coordinate.getX())
                        .filter(filter -> filter.getY() > Math.min(coordinate.getY(), kingCoordinate.getY()))
                        .filter(filter -> filter.getY() < Math.max(coordinate.getY(), kingCoordinate.getY()))
                        .toList();
                if (coordinateList.isEmpty()) {
                    return true;
                }
            }else if (coordinate.getY() == kingCoordinate.getY()) {
                List<Coordinate> coordinateList = coordinates.stream()
                        .filter(filter -> filter.getY() == coordinate.getY())
                        .filter(filter -> filter.getX() > Math.min(coordinate.getX(), kingCoordinate.getX()))
                        .filter(filter -> filter.getX() < Math.max(coordinate.getX(), kingCoordinate.getX()))
                        .toList();
                if (coordinateList.isEmpty()) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean checkCannon(Coordinate kingCoordinate, List<Coordinate> coordinates) {
        List<Coordinate> list = coordinates.stream().filter(filter -> filter.getPurpose().equals(ChessLabel.Purpose.CANNON) && filter.getColor().equals(kingCoordinate.enemyColor()))
                .filter(filter -> filter.getX() == kingCoordinate.getX() || filter.getY() == kingCoordinate.getY())
                .toList();
        for (Coordinate coordinate : list) {
            if (coordinate.getX() == kingCoordinate.getX()) {
                List<Coordinate> coordinateList = coordinates.stream()
                        .filter(filter -> filter.getX() == coordinate.getX())
                        .filter(filter -> filter.getY() > Math.min(coordinate.getY(), kingCoordinate.getY()))
                        .filter(filter -> filter.getY() < Math.max(coordinate.getY(), kingCoordinate.getY()))
                        .toList();
                if (coordinateList.size() == 1) {
                    return true;
                }
            }else if (coordinate.getY() == kingCoordinate.getY()) {
                List<Coordinate> coordinateList = coordinates.stream()
                        .filter(filter -> filter.getY() == coordinate.getY())
                        .filter(filter -> filter.getX() > Math.min(coordinate.getX(), kingCoordinate.getX()))
                        .filter(filter -> filter.getX() < Math.max(coordinate.getX(), kingCoordinate.getX()))
                        .toList();
                if (coordinateList.size() == 1) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean checkPawn(Coordinate kingCoordinate, List<Coordinate> coordinates) {
        List<Coordinate> list = coordinates.stream().filter(filter -> filter.getPurpose().equals(ChessLabel.Purpose.PAWN) && filter.getColor().equals(kingCoordinate.enemyColor())).toList();
        // 上方
        if (kingCoordinate.getY() <= 5) {
            // 高于当前帅的位置
            list = list.stream().filter(filter -> filter.getY() >= kingCoordinate.getY() && (filter.getX() == kingCoordinate.getX() || filter.getY() == kingCoordinate.getY()))
                    // 距离帅一格范围
                    .filter(filter -> {
                        int absoluteX = absolute(filter.getX() - kingCoordinate.getX());
                        int absoluteY = absolute(filter.getY() - kingCoordinate.getY());
                        return absolute(absoluteX - absoluteY) <= 1;
                    })
                    .toList();
            return !list.isEmpty();
        }
        // 下方
        if (kingCoordinate.getY() >= 6) {
                    // 高于当前帅的位置
            list = list.stream().filter(filter -> filter.getY() <= kingCoordinate.getY() && (filter.getX() == kingCoordinate.getX() || filter.getY() == kingCoordinate.getY()))
                    // 距离帅一格范围
                    .filter(filter -> {
                        int absoluteX = absolute(filter.getX() - kingCoordinate.getX());
                        int absoluteY = absolute(filter.getY() - kingCoordinate.getY());
                        return absolute(absoluteX - absoluteY) <= 1;
                    })
                    .toList();
            return !list.isEmpty();
        }
        return false;
    }

    private static int absolute(int value) {
        return value < 0 ? value * -1 : value;
    }
}
